#include "main.h"
#include "fm33le0xx_fl.h"
#include "mcu_uart.h"
#include "sys_user.h"
#include "fm33le0xx_fl_flash.h"

#define APPLICATION_ADDRESS_OFFSET 0x2800
#define APPLICATION_CONFIG_ADDR 0x2600
// 处于APP内部， 储存于APP区末尾区域
#define APPLICATION_VERSION_PTR 0xb800

#define MAGIC_NUM 0x6e

enum {
    // boot cmd
    CMD_WRITE_FLASH = 0x000a,
    CMD_OTA_FINISH = 0x000b,
    CMD_INQUIRY_VERSION = 0x000c,
    CMD_HOLD_BOOT = 0x000d,             // BOOT页面时间保持时间设置为1s
    CMD_EXIT_BOOT = 0x000e,             // 退出BOOT页面

    // app cmd
    CMD_RESET = 0x0019,                 // FM33复位
    CMD_STATUS_TO_ESP32 = 0x001a,       // 消息上报到ESP32
    CMD_STATUS_TO_FM33 = 0x001b,        // 下发消息到FM33
    CMD_SEND_TIME_TO_FM33 = 0x001e,     // 下发时间到FM33
};

enum {
    CMD_EXEC_FINISH = 0x0000,
    CMD_EXEC_FAILED = 0x0001,
    CMD_EXEC_ADDR_ERROR = 0x0002,
    CMD_EXEC_DATA_LEN_ERROR = 0x0003,
    CMD_EXEC_WRITE_FLASH_ERROR = 0x0004,
    CMD_EXEC_XOR_FLASH_ERROR = 0x0005,
};

// total 512 byte
typedef struct {
    uint8_t version_major: 8;   // 主要版本号
    uint8_t version_minor: 8;   // 次要版本号
    uint8_t magic_num:     8;   // 用于确保不会出现XOR RESULT误校验
    uint8_t xor_result:    8;
    uint8_t reserve2[508];
} app_info_t;

// ESP32下发下载固件
typedef struct {
    uint16_t reserve;
    uint16_t mask;
    uint32_t flash_addr;
    uint32_t data_len;
    uint8_t data[0]; // 地址必须是32位对齐, 偏移要整除32位
} __attribute__ ((packed)) ota_data_t;

// ESP32下发与OTA成功消息，校验与写入版本号
typedef struct {
    uint8_t version_major;
    uint8_t version_minor;
    uint16_t mask;
    uint32_t flash_addr;
    uint32_t data_len;
    uint32_t xor_result;
} __attribute__ ((packed)) ota_finish_data_t;

// FM33回复ESP32消息
typedef struct {
    uint16_t error_code: 7;
    uint16_t in_boot: 1;
    uint16_t boot_version: 8;
    uint16_t mask;
    /* data */
} __attribute__ ((packed)) cmd_rsp_t;

uint8_t noinit_value[256] __attribute__( ( section( ".bss.NoInit")) ) ;

// 标记APP区域是有效的 
static uint8_t app_intact_mask;
static uint32_t cmd_last_time = 0;
uint32_t boot_hold_time = 200;
const app_info_t* app_info;
static uint8_t boot_version[] = {0x00, 0x01};

void jump_to_user_app() {
    uint32_t jump_address = *(__IO uint32_t*) (APPLICATION_ADDRESS_OFFSET + 4);
    __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS_OFFSET);
    (*( void (*)( ) )jump_address)();
}

// user-app: version in 0x2e00
uint8_t user_app_mask_page_is_intact() {
    const uint8_t* app_version = (uint8_t *)APPLICATION_VERSION_PTR;
    if ((app_version[0] + app_version[1]) != app_version[2]) {
        user_printf("app data is error: %d + %d != %d\r\n", app_version[0], app_version[1], app_version[2]);
        return 0;
    }

    if (app_info->magic_num != MAGIC_NUM || (app_info->magic_num ^ app_info->version_major ^ app_info->version_minor) != app_info->xor_result) {
        user_printf("app info page is not correct\r\n");
        return 0;
    }

    if (app_info->version_major != app_version[0] ||  app_info->version_minor != app_version[1]) {
        user_printf("app version not same: %d.%d != %d.%d \r\n", app_info->version_major, app_info->version_minor, app_version[0], app_version[1]);
        return 0;
    }

    return 1;
}

// 每次写FLASH的时候都需要擦除app的
void user_app_erase_mask_page() {
    static uint8_t erase_mask = 0;
    if (erase_mask == 1) {
        return ;
    }
    erase_mask = 1;
    FL_FLASH_PageErase(FLASH, APPLICATION_CONFIG_ADDR);
}

// OTA完成后写入验证信息
void user_app_write_mask_page(uint8_t version_major, uint8_t version_minor) {
    app_info_t app_info_data;
    memset(&app_info_data, 0xff, sizeof(app_info_t));
    app_info_data.version_major = version_major;
    app_info_data.version_minor = version_minor;
    app_info_data.magic_num = MAGIC_NUM;
    app_info_data.xor_result = app_info_data.version_major ^ app_info_data.version_minor ^ app_info_data.magic_num;
    FL_FLASH_PageErase(FLASH, APPLICATION_CONFIG_ADDR);
    FL_FLASH_Program_Page(FLASH, APPLICATION_CONFIG_ADDR / FL_FLASH_PAGE_SIZE_BYTE, (uint32_t *)&app_info_data);
}

void frame_recv_callback(int cmd, const uint8_t* buf, int len) {
    cmd_rsp_t cmd_rsp = {
        .error_code = CMD_EXEC_FAILED,
        .in_boot = 1,
        .mask = 0, 
    };

    if (cmd == CMD_WRITE_FLASH && len > sizeof(ota_data_t)) {
        cmd_last_time = sys_clock_get_ms();
        app_intact_mask = 0;
        user_app_erase_mask_page();

        const ota_data_t* ota_data = (ota_data_t *)buf;
        cmd_rsp.mask = ota_data->mask;
        user_printf("recv write flash, addr: %04x, len: %d\r\n", ota_data->flash_addr, ota_data->data_len);
        len = len - sizeof(ota_data_t);
        // 数据长度一定是：FL_FLASH_PAGE_SIZE_BYTE
        if (len != ota_data->data_len || len != FL_FLASH_PAGE_SIZE_BYTE) {
            cmd_rsp.error_code = CMD_EXEC_DATA_LEN_ERROR;
            goto cmd_respond;
        }
         
        // 地址不能覆盖bootloader而且地址一定是要4字节对齐
        if (ota_data->flash_addr < APPLICATION_ADDRESS_OFFSET || ota_data->flash_addr % FL_FLASH_PAGE_SIZE_BYTE != 0) {
            cmd_rsp.error_code = CMD_EXEC_ADDR_ERROR;
            goto cmd_respond;
        }

        if (FL_FLASH_PageErase(FLASH, ota_data->flash_addr) == FL_FAIL || FL_FLASH_Program_Page(FLASH, ota_data->flash_addr / FL_FLASH_PAGE_SIZE_BYTE, (uint32_t *)ota_data->data) == FL_FAIL) {
            cmd_rsp.error_code = CMD_EXEC_WRITE_FLASH_ERROR;
            goto cmd_respond;
        }
        
        cmd_rsp.error_code = CMD_EXEC_FINISH;
    } else if (cmd == CMD_OTA_FINISH && len >= sizeof(ota_finish_data_t)) {
        const ota_finish_data_t* ota_finish_data = (ota_finish_data_t *)buf;
        cmd_rsp.mask = ota_finish_data->mask;

        if (ota_finish_data->flash_addr % 4) {
            cmd_rsp.error_code = CMD_EXEC_ADDR_ERROR;
            goto cmd_respond;
        }
        if (ota_finish_data->data_len % 4) {
            cmd_rsp.error_code = CMD_EXEC_DATA_LEN_ERROR;
            goto cmd_respond;
        }

        const uint32_t* xor_addr = (uint32_t *)ota_finish_data->flash_addr;
        uint32_t xor_result = 0x00;
        uint16_t data_nums = ota_finish_data->data_len / 4;
        for (uint16_t i = 0; i < data_nums; i++) {
            xor_result = xor_result ^ xor_addr[i];
        }
        if (xor_result != ota_finish_data->xor_result) {
            cmd_rsp.error_code = CMD_EXEC_XOR_FLASH_ERROR;
            goto cmd_respond;
        }
        const uint8_t* app_version = (uint8_t *)APPLICATION_VERSION_PTR;
        if (app_version[0] != ota_finish_data->version_major || app_version[1] != ota_finish_data->version_minor) {
            cmd_rsp.error_code = CMD_EXEC_FAILED;
            user_printf("OTA Failed version not same \r\n");
            goto cmd_respond;
        }

        user_app_write_mask_page(ota_finish_data->version_major, ota_finish_data->version_minor);
        user_printf("OTA Success, version: %d.%d \r\n", ota_finish_data->version_major, ota_finish_data->version_minor);
        app_intact_mask = 1;
        cmd_rsp.error_code = CMD_EXEC_FINISH;

        cmd_last_time = sys_clock_get_ms();
        boot_hold_time = 200;
    } else if (cmd == CMD_INQUIRY_VERSION) {
        cmd_rsp.error_code = CMD_EXEC_FINISH;
        if (app_intact_mask) {
            cmd_rsp.mask = (app_info->version_major << 8) | app_info->version_minor; 
        }
    } else if (cmd == CMD_HOLD_BOOT) {
        cmd_last_time = sys_clock_get_ms();
        boot_hold_time = 1000;
        cmd_rsp.error_code = CMD_EXEC_FINISH; 
    } else if (cmd == CMD_EXIT_BOOT) {
        boot_hold_time = 0;
        cmd_rsp.error_code = CMD_EXEC_FINISH; 
    } else {
        return ;
    }

cmd_respond:
    cmd_rsp.boot_version = (boot_version[0] << 4) | boot_version[1];
    mcu_write_bytes(cmd | 0x8000, (uint8_t *)&cmd_rsp, sizeof(cmd_rsp_t));
}

int main(void) {
    user_log_init();
    sys_clock_init();
    sys_nvic_init();
    sys_hardware_init();
    mcu_uart_init(frame_recv_callback);   // 与ESP32通讯

    app_info = (app_info_t*)APPLICATION_CONFIG_ADDR;
    if (user_app_mask_page_is_intact()) {
        app_intact_mask = 1;
        user_printf("bootloader start, app version: %d.%d \r\n", app_info->version_major, app_info->version_minor);
    } else {
        app_intact_mask = 0;
        user_printf("bootloader start, not found app\r\n");
    }

    if (noinit_value[0] == ~noinit_value[1]) {
        user_printf("noinit value not used \r\n");
    } else {
        noinit_value[0] = 0x06;
        noinit_value[1] = ~noinit_value[0];
        user_printf("noinit value used \r\n");
    }

    // 用于发送版本号或者唤醒设备
    cmd_rsp_t cmd_rsp = {
        .error_code = CMD_EXEC_FINISH,
        .in_boot = 1,
        .boot_version = (boot_version[0] << 4) | boot_version[1],
        .mask = 0,
    };
    if (app_intact_mask) {
        cmd_rsp.mask = (app_info->version_major << 8) | app_info->version_minor;
    }
    mcu_write_bytes(CMD_INQUIRY_VERSION, (uint8_t *)&cmd_rsp, sizeof(cmd_rsp_t));

    cmd_last_time = sys_clock_get_ms();

    boot_hold_time = 500;
    // 停留等待命令空闲500ms进行等待升级
    while (app_intact_mask == 0 || sys_clock_get_ms() - cmd_last_time < boot_hold_time) {
       mcu_deal_rx_fifo_data();
    }

    user_printf("bootloader jump to user app\r\n");
    // 等待串口数据DMA发送完成
    sys_clock_delay_ms(2);
    jump_to_user_app();
}
